using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
using Autofac;
using Koonfusion.SyncDeploy.Core.Manifest;
using Koonfusion.SyncDeploy.Core.Services;

namespace Koonfusion.SyncDeploy.Core
{
    public class ManifestBuilder : ManifestProcessorBase
    {
        private bool _isUsed = false;

        public override event EventHandler OnStart;
        public override event EventHandler OnSuccess;
        public override event FileProcessedEventHandler OnFileProcessed;

        public String Name { get; private set; }
        public String Version { get; private set; }
        public DirectoryInfo Source { get; private set; }
        public DirectoryInfo Destination { get; private set; }
        public FileInfo[] SourceFileList { get; private set; }
        public FileInfo ManifestFile { get; private set; }
        public SyncManifest Manifest { get; private set; }

        public FileInfo CreatePackage(DirectoryInfo source, DirectoryInfo destination, String name, String version)
        {
            #region Validation
            if (_isUsed) throw new InvalidOperationException("This instance has already been used to generate a package. Create a new instance and try again.");

            if (source == null) throw new ArgumentNullException("source");
            source.Refresh();
            if (!source.Exists) throw new DirectoryNotFoundException();

            if (destination == null) throw new ArgumentNullException("destination");
            destination.Refresh();

            if (name == null) throw new ArgumentNullException("name");
            if (source.FullName == destination.FullName) throw new ArgumentException("source and destination can not be the same");
            
            #endregion

            if (!destination.Exists) destination.Create();

            _isUsed = true;
            Name = name;
            Version = version;
            Source = source;
            Destination = destination;
           
            SourceFileList = Source.GetFiles("*.*", SearchOption.AllDirectories);
            if (SourceFileList.Length == 0) throw new ArgumentException("source folder is empty");

            ManifestFile = new FileInfo(Path.Combine(Destination.FullName.Trim('\\'), "manifest.xml"));

            if (OnStart != null)
                OnStart(this, null);

            Manifest = new SyncManifest
            {
                Files = new List<SyncFile>(),
                Name = Name,
                GeneratedOn = DateTime.UtcNow,
                SyncDefVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString()
            };

            AddFilesToPackage();

            new ManifestService().Write(Manifest, ManifestFile);

            if (OnSuccess != null)
                OnSuccess(this, null);

            return ManifestFile;
        }

        public FileInfo CreatePackage(DirectoryInfo source, DirectoryInfo destination, String name)
        {
            return CreatePackage(source, destination, name, string.Empty);
        }



        private void AddFilesToPackage()
        {
            foreach (var file in SourceFileList)
            {
                var currentFile = new SyncFile
                {
                    Hash = FileUtilities.GetHash(file),
                    RawSize = file.Length,
                    Target = "\\" + FileUtilities.GetReletive(Source, file)
                };

                var zipFile = new FileInfo(Path.Combine(Destination.FullName, currentFile.Hash) + ".zip");

                if (!zipFile.Exists)
                {
                    FileUtilities.CompressFile(file, zipFile);
                    zipFile.Refresh();
                }

                currentFile.ZipSize = zipFile.Length;
                Manifest.Files.Add(currentFile);

                if (OnFileProcessed != null) OnFileProcessed(this, file);
            }
        }



    }
}